Individual Assignment #2: ARIMA Lab.

Due: Nov. 23 before class time

(40 points)


The file titled US Electricity.csv includes a time series index compiled by the US Federal Reserve representing total fossil-fuel US electricity generation by all utilities from January 1939 through October 2021.

In the following code box we read the CSV file and set up the data as a tsibble and then we plot it and subset it to examine it.

#clear environment
rm(list = ls())
library(fpp3)

D <- read.csv("US Electricity.csv") %>% 
  mutate(DATE = yearmonth(DATE)) %>%
  as_tsibble(index = DATE)
  
D %>% autoplot(ELEC)


DR <- D %>% filter(DATE >= yearmonth("2010 Jan"))

DR %>% autoplot(ELEC)

We are interested in developing a two-year long monthly forecast (24 months) for the national electricity production requirements.

  1. Examine the stationarity of the ELEC time series in the reduced DR data, examine also the corresponding ACF and PACF diagrams and propose three plausible ARIMA models to fit the data.
#stationarity of DR - not stationary, need to add seasonality  
#ACF and PACF
DR %>% gg_tsdisplay(ELEC, plot_type = "partial") +
  labs(title="DR data", y="")



#add seasonality
DR.12 <- DR %>% gg_tsdisplay(difference(ELEC, 12),
               plot_type='partial', lag=60) +
  labs(title="Seasonally differenced - 12", y="")
DR.12
Warning: Removed 12 row(s) containing missing values (geom_path).
Warning: Removed 12 rows containing missing values (geom_point).

DR.4 <- DR %>% gg_tsdisplay(difference(ELEC, 4),
               plot_type='partial', lag=60) +
  labs(title="Seasonally differenced - 4", y="")
DR.4
Warning: Removed 4 row(s) containing missing values (geom_path).
Warning: Removed 4 rows containing missing values (geom_point).

#DR.d %>% ACF(ELEC) %>%
  #autoplot() + labs(title = 'Electricity 2010 - 2020')
#DR.d %>% autoplot(.vars = diff2.E)

#DR.d %>% gg_tsdisplay(ELEC, plot_type = "partial")  

DR is not stationary, all of the lags are significant lags in the ACF plot. We need to figure out how many differences to take until it is stationary. PACF shows only 2 very large significant lags at the beginning. Because the ACF is complex, and the PACF dies down slowly, I believe this a Moving Average forecasting model would best suite this data. It also looks like the ACF is showing some seasonality in the lags, as there is a pattern to the spikes. This makes sense as your electricity usage changes as the weather changes.

The PACF dies down so we will identify this model as a MA(1) or MA(2) seeing if seasonal MA or regular MA results in a better arima model. I think it could possibly be seasonality 4, rather than seasonality 12 as well. 1) ARIMA(1,1,1)(1,1,1) [12] 2) ARIMA(1,1,2)(1,1,1) [12] 3) ARIMA(1,1,1)(1,1,2) [12]

  1. Using fable fit the following five models to the DR data: (i)-(iii) the three models you propose in (1), (iv) the automatically selected model by the ARIMA() function, and (v) the automatically selected model by the ETS() function. Report the name/order of each model and the corresponding AICc and BIC.
#how many differences it needs
DR %>% features(ELEC, unitroot_nsdiffs) #seasonal differences needed = 1
DR %>% features(ELEC,unitroot_ndiffs) #differences needed = 0

fitDR <- DR %>%
  model( #none of the models worked with d = 0 
    arima1 = ARIMA(ELEC ~ pdq(1,1,1) + PDQ(1,1,1)),
    arima2 = ARIMA(ELEC ~ pdq(1,1,2) + PDQ(1,1,1)),
    arima3 = ARIMA(ELEC ~ pdq(1,1,1) + PDQ(1,1,2)),
    auto.arima = ARIMA(ELEC), #100210
    auto.ETS = ETS(ELEC)) #error("M") + trend("N") + season("A")
    
fitDR %>% pivot_longer(everything(), names_to = "Model name",
                     values_to = "Orders")

glance(fitDR) %>% arrange(AICc) %>% select(.model:BIC)
  1. Examine the residuals of all the models using the Ljung-Box test and the gg_tsresiduals() function. Is there a validity problem with any of the models?
fitDR %>% augment() %>%
  features(.resid, ljung_box, lag = 60)

fitDR %>% select(arima1) %>% gg_tsresiduals()

fitDR %>% select(arima2) %>% gg_tsresiduals()

fitDR %>% select(arima3) %>% gg_tsresiduals() 

fitDR %>% select(auto.arima) %>% gg_tsresiduals()

fitDR %>% select(auto.ETS) %>% gg_tsresiduals()

  1. For the set of five models selected (automatically and/or manually) examine the in-sample accuracy metrics. Based on a holistic analysis of the information criteria select the best two ARIMA models and the ETS model. Report the model name/order and their parameter values.

For model cross-validation purposes stretch the DR data as follows:

fitDR %>% accuracy() %>% select(.model, MAPE, RMSE, MAE)
fitDR %>% glance() %>%
  select(.model, AIC, AICc, BIC)

Best 2 arima Models = arima3 ARIMA(1,1,1)(1,1,2) [12] and arima1 ARIMA(1,1,1)(1,1,1) [12] ETS model = Elec ~ error(“M”) + trend(“N”) + season(“A”)

  1. Fit cross-validation models for each of the time sub-series in the stretched data for each of the four model types selected in (4). In the case(s) where the models were automatically selected, do NOT run the automatic selection under cross validation, instead enter manually the model order/type when you call the ARIMA()/ETS() function.
#kinda takes awhile
#given code
DR.CV <- DR %>%
  filter(DATE >= yearmonth("2010 Jan")) %>%
  stretch_tsibble(.init = 36, .step = 1)

mC <- DR.CV %>% 
  model(
    arima1 = ARIMA(ELEC ~ pdq(0,1,1) + PDQ(0,1,1)),
    arima2 = ARIMA(ELEC ~ pdq(0,1,2) + PDQ(0,1,1)),
    arima3 = ARIMA(ELEC ~ pdq(0,1,2) + PDQ(0,1,2)),
    ETS(ELEC ~ error("M") + trend("N") + season("A"))) 
Warning in max(PDQ$Q) : no non-missing arguments to max; returning -Inf
Warning in max(PDQ$Q) : no non-missing arguments to max; returning -Inf
Warning in max(PDQ$Q) : no non-missing arguments to max; returning -Inf
Warning in max(PDQ$Q) : no non-missing arguments to max; returning -Inf
Warning in max(PDQ$Q) : no non-missing arguments to max; returning -Inf
Warning in max(PDQ$Q) : no non-missing arguments to max; returning -Inf
Warning in max(PDQ$Q) : no non-missing arguments to max; returning -Inf
Warning in max(PDQ$Q) : no non-missing arguments to max; returning -Inf
Warning in max(PDQ$Q) : no non-missing arguments to max; returning -Inf
Warning in max(PDQ$Q) : no non-missing arguments to max; returning -Inf
Warning in max(PDQ$Q) : no non-missing arguments to max; returning -Inf
Warning in max(PDQ$Q) : no non-missing arguments to max; returning -Inf
Warning in max(PDQ$Q) : no non-missing arguments to max; returning -Inf
Warning in max(PDQ$Q) : no non-missing arguments to max; returning -Inf
Warning in max(PDQ$Q) : no non-missing arguments to max; returning -Inf
Warning in max(PDQ$Q) : no non-missing arguments to max; returning -Inf
Warning in max(PDQ$Q) : no non-missing arguments to max; returning -Inf
Warning in max(PDQ$Q) : no non-missing arguments to max; returning -Inf
Warning in max(PDQ$Q) : no non-missing arguments to max; returning -Inf
Warning in max(PDQ$Q) : no non-missing arguments to max; returning -Inf
Warning in max(PDQ$Q) : no non-missing arguments to max; returning -Inf
Warning in max(PDQ$Q) : no non-missing arguments to max; returning -Inf
Warning in max(PDQ$Q) : no non-missing arguments to max; returning -Inf
Warning in max(PDQ$Q) : no non-missing arguments to max; returning -Inf
Warning in max(PDQ$Q) : no non-missing arguments to max; returning -Inf
Warning in max(PDQ$Q) : no non-missing arguments to max; returning -Inf
Warning in max(PDQ$Q) : no non-missing arguments to max; returning -Inf
Warning in max(PDQ$Q) : no non-missing arguments to max; returning -Inf
Warning in max(PDQ$Q) : no non-missing arguments to max; returning -Inf
Warning in max(PDQ$Q) : no non-missing arguments to max; returning -Inf
Warning in max(PDQ$Q) : no non-missing arguments to max; returning -Inf
Warning in max(PDQ$Q) : no non-missing arguments to max; returning -Inf
Warning in max(PDQ$Q) : no non-missing arguments to max; returning -Inf
Warning in max(PDQ$Q) : no non-missing arguments to max; returning -Inf
Warning in max(PDQ$Q) : no non-missing arguments to max; returning -Inf
Warning in max(PDQ$Q) : no non-missing arguments to max; returning -Inf
Warning: 36 errors (1 unique) encountered for arima3
[36] Could not find an appropriate ARIMA model.
This is likely because automatic selection does not select models with characteristic roots that may be numerically unstable.
For more details, refer to https://otexts.com/fpp3/arima-r.html#plotting-the-characteristic-roots
mC %>% 
  forecast(h = 4) %>% #each model ID you have 4 forecasts 
  group_by(.id, .model) %>% #ID of one corresponds to 3 models
  mutate(h = row_number()) %>% #create variable h for the .id # 
  ungroup() -> fCV #forecast cross validation 
fCV
  1. Prepare a 24-month ahead forecast foe each of the models fitted in (5) and prepare a plot of MAPE vs months-ahead. Based on the dynamic behavior of cross-validation MAPE discuss which model(s) should be kept/discarded.
fCV <- mC %>% 
  forecast(h = 24) %>% #for each of the 4 model IDs you have 24 forecasts 
  group_by(.id, .model) %>% #ID of one corresponds to 3 models
  mutate(h = row_number()) %>% #create variable h for the .id # 
  ungroup() #forecast cross validation 

fCV %>%
  accuracy(D, by = c("h", ".model")) %>% #shows you all the CV metrics
  ggplot(aes(x = h, y = MAPE, color = .model)) +
  geom_line()
Warning: The future dataset is incomplete, incomplete out-of-sample data will be treated as missing. 
24 observations are missing between 2021 Oct and 2023 Sep

fCV %>%
  accuracy(D, by = c("h", ".model")) %>%
  ggplot(aes(x = h, y = RMSE, color = .model)) +
  geom_line()
Warning: The future dataset is incomplete, incomplete out-of-sample data will be treated as missing. 
24 observations are missing between 2021 Oct and 2023 Sep

  1. Examine the cross-validation residuals of the models you selected in (6), and based on their correlation (model vs. model) discuss if it is advisable to prepare an ensemble forecast averaging the forecasts of two or more models.
DR.CV <- DR %>%
  filter(DATE >= yearmonth("2010 Jan")) %>%
  stretch_tsibble(.init = 36, .step = 1)

DR.CV %>% 
  model(
    ARIMA(ELEC ~ pdq(0,1,2) + PDQ(0,1,2)),
    ETS(ELEC ~ error("M") + trend("N") + season("A"))) %>%
  forecast(h = 24) %>%
  accuracy(DR) %>%
  select(.model, RMSE:MAPE)
Warning in max(PDQ$Q) : no non-missing arguments to max; returning -Inf
Warning in max(PDQ$Q) : no non-missing arguments to max; returning -Inf
Warning in max(PDQ$Q) : no non-missing arguments to max; returning -Inf
Warning in max(PDQ$Q) : no non-missing arguments to max; returning -Inf
Warning in max(PDQ$Q) : no non-missing arguments to max; returning -Inf
Warning in max(PDQ$Q) : no non-missing arguments to max; returning -Inf
Warning in max(PDQ$Q) : no non-missing arguments to max; returning -Inf
Warning in max(PDQ$Q) : no non-missing arguments to max; returning -Inf
Warning in max(PDQ$Q) : no non-missing arguments to max; returning -Inf
Warning in max(PDQ$Q) : no non-missing arguments to max; returning -Inf
Warning in max(PDQ$Q) : no non-missing arguments to max; returning -Inf
Warning in max(PDQ$Q) : no non-missing arguments to max; returning -Inf
Warning in max(PDQ$Q) : no non-missing arguments to max; returning -Inf
Warning in max(PDQ$Q) : no non-missing arguments to max; returning -Inf
Warning in max(PDQ$Q) : no non-missing arguments to max; returning -Inf
Warning in max(PDQ$Q) : no non-missing arguments to max; returning -Inf
Warning in max(PDQ$Q) : no non-missing arguments to max; returning -Inf
Warning in max(PDQ$Q) : no non-missing arguments to max; returning -Inf
Warning in max(PDQ$Q) : no non-missing arguments to max; returning -Inf
Warning in max(PDQ$Q) : no non-missing arguments to max; returning -Inf
Warning in max(PDQ$Q) : no non-missing arguments to max; returning -Inf
Warning in max(PDQ$Q) : no non-missing arguments to max; returning -Inf
Warning in max(PDQ$Q) : no non-missing arguments to max; returning -Inf
Warning in max(PDQ$Q) : no non-missing arguments to max; returning -Inf
Warning in max(PDQ$Q) : no non-missing arguments to max; returning -Inf
Warning in max(PDQ$Q) : no non-missing arguments to max; returning -Inf
Warning in max(PDQ$Q) : no non-missing arguments to max; returning -Inf
Warning in max(PDQ$Q) : no non-missing arguments to max; returning -Inf
Warning in max(PDQ$Q) : no non-missing arguments to max; returning -Inf
Warning in max(PDQ$Q) : no non-missing arguments to max; returning -Inf
Warning in max(PDQ$Q) : no non-missing arguments to max; returning -Inf
Warning in max(PDQ$Q) : no non-missing arguments to max; returning -Inf
Warning in max(PDQ$Q) : no non-missing arguments to max; returning -Inf
Warning in max(PDQ$Q) : no non-missing arguments to max; returning -Inf
Warning in max(PDQ$Q) : no non-missing arguments to max; returning -Inf
Warning in max(PDQ$Q) : no non-missing arguments to max; returning -Inf
Warning: 36 errors (1 unique) encountered for ARIMA(ELEC ~ pdq(0, 1, 2) + PDQ(0, 1, 2))
[36] Could not find an appropriate ARIMA model.
This is likely because automatic selection does not select models with characteristic roots that may be numerically unstable.
For more details, refer to https://otexts.com/fpp3/arima-r.html#plotting-the-characteristic-roots

Warning: The future dataset is incomplete, incomplete out-of-sample data will be treated as missing. 
24 observations are missing between 2021 Oct and 2023 Sep
  1. The index is very useful for energy planning purpose as most of the variability and seasonality is produced by combined cycle natural gas plants and single cycle peaker plants that also run on natural gas (i.e., nuclear and coal generation is fixed and relatively constant). For this purpose it is of interest to know what is the production index level that will not be superated with a probability (service-level) of 95%. For the best model in (6) plot the 24-month ahead forecast and plot the forecast and the corresponding confidence interval to help you address the service level question. Report numerically the month-by-month the index forecasts that meet the desired 95% service level.
LS0tCnRpdGxlIDogIklBIC0gMiBDYXNleSBDb3BlbGFuZCIgCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KKioqCjxjZW50ZXI+CiMjIEluZGl2aWR1YWwgQXNzaWdubWVudCAjMjogQVJJTUEgTGFiLgojIyMjIER1ZTogTm92LiAyMyBiZWZvcmUgY2xhc3MgdGltZQojIyMjICg0MCBwb2ludHMpCjwvY2VudGVyPgoqKioKClRoZSBmaWxlIHRpdGxlZCAqKlVTIEVsZWN0cmljaXR5LmNzdioqIGluY2x1ZGVzIGEgdGltZSBzZXJpZXMgaW5kZXggY29tcGlsZWQgYnkgdGhlIFVTIEZlZGVyYWwgUmVzZXJ2ZSByZXByZXNlbnRpbmcgdG90YWwgZm9zc2lsLWZ1ZWwgVVMgZWxlY3RyaWNpdHkgZ2VuZXJhdGlvbiBieSBhbGwgdXRpbGl0aWVzIGZyb20gSmFudWFyeSAxOTM5IHRocm91Z2ggT2N0b2JlciAyMDIxLgoKSW4gdGhlIGZvbGxvd2luZyBjb2RlIGJveCB3ZSByZWFkIHRoZSBDU1YgZmlsZSBhbmQgc2V0IHVwIHRoZSBkYXRhIGFzIGEgKnRzaWJibGUqIGFuZCB0aGVuIHdlIHBsb3QgaXQgYW5kIHN1YnNldCBpdCB0byBleGFtaW5lIGl0LgoKYGBge3J9CiNjbGVhciBlbnZpcm9ubWVudApybShsaXN0ID0gbHMoKSkKYGBgCgpgYGB7cn0KbGlicmFyeShmcHAzKQoKRCA8LSByZWFkLmNzdigiVVMgRWxlY3RyaWNpdHkuY3N2IikgJT4lIAogIG11dGF0ZShEQVRFID0geWVhcm1vbnRoKERBVEUpKSAlPiUKICBhc190c2liYmxlKGluZGV4ID0gREFURSkKICAKRCAlPiUgYXV0b3Bsb3QoRUxFQykKCkRSIDwtIEQgJT4lIGZpbHRlcihEQVRFID49IHllYXJtb250aCgiMjAxMCBKYW4iKSkKCkRSICU+JSBhdXRvcGxvdChFTEVDKQpgYGAKCldlIGFyZSBpbnRlcmVzdGVkIGluIGRldmVsb3BpbmcgYSB0d28teWVhciBsb25nIG1vbnRobHkgZm9yZWNhc3QgKDI0IG1vbnRocykgZm9yIHRoZSBuYXRpb25hbCBlbGVjdHJpY2l0eSBwcm9kdWN0aW9uIHJlcXVpcmVtZW50cy4gCgoKMS4gRXhhbWluZSB0aGUgc3RhdGlvbmFyaXR5IG9mIHRoZSAqKkVMRUMqKiB0aW1lIHNlcmllcyBpbiB0aGUgcmVkdWNlZCAqKkRSKiogZGF0YSwgZXhhbWluZSBhbHNvIHRoZSBjb3JyZXNwb25kaW5nIEFDRiBhbmQgUEFDRiBkaWFncmFtcyBhbmQgcHJvcG9zZSB0aHJlZSBwbGF1c2libGUgQVJJTUEgbW9kZWxzIHRvIGZpdCB0aGUgZGF0YS4KYGBge3J9CiNzdGF0aW9uYXJpdHkgb2YgRFIgLSBub3Qgc3RhdGlvbmFyeSwgbmVlZCB0byBhZGQgc2Vhc29uYWxpdHkgIAojQUNGIGFuZCBQQUNGCkRSICU+JSBnZ190c2Rpc3BsYXkoRUxFQywgcGxvdF90eXBlID0gInBhcnRpYWwiKSArCiAgbGFicyh0aXRsZT0iRFIgZGF0YSIsIHk9IiIpCgoKI2FkZCBzZWFzb25hbGl0eQpEUi4xMiA8LSBEUiAlPiUgZ2dfdHNkaXNwbGF5KGRpZmZlcmVuY2UoRUxFQywgMTIpLAogICAgICAgICAgICAgICBwbG90X3R5cGU9J3BhcnRpYWwnLCBsYWc9NjApICsKICBsYWJzKHRpdGxlPSJTZWFzb25hbGx5IGRpZmZlcmVuY2VkIC0gMTIiLCB5PSIiKQpEUi4xMgoKRFIuNCA8LSBEUiAlPiUgZ2dfdHNkaXNwbGF5KGRpZmZlcmVuY2UoRUxFQywgNCksCiAgICAgICAgICAgICAgIHBsb3RfdHlwZT0ncGFydGlhbCcsIGxhZz02MCkgKwogIGxhYnModGl0bGU9IlNlYXNvbmFsbHkgZGlmZmVyZW5jZWQgLSA0IiwgeT0iIikKRFIuNAogCgoKYGBgCkRSIGlzIG5vdCBzdGF0aW9uYXJ5LCBhbGwgb2YgdGhlIGxhZ3MgYXJlIHNpZ25pZmljYW50IGxhZ3MgaW4gdGhlIEFDRiBwbG90LiBXZSBuZWVkIHRvIGZpZ3VyZSBvdXQgaG93IG1hbnkgZGlmZmVyZW5jZXMgdG8gdGFrZSB1bnRpbCBpdCBpcyBzdGF0aW9uYXJ5LiBQQUNGIHNob3dzIG9ubHkgMiB2ZXJ5IGxhcmdlIHNpZ25pZmljYW50IGxhZ3MgYXQgdGhlIGJlZ2lubmluZy4gQmVjYXVzZSB0aGUgQUNGIGlzIGNvbXBsZXgsIGFuZCB0aGUgUEFDRiBkaWVzIGRvd24gc2xvd2x5LCBJIGJlbGlldmUgdGhpcyBhIE1vdmluZyBBdmVyYWdlIGZvcmVjYXN0aW5nIG1vZGVsIHdvdWxkIGJlc3Qgc3VpdGUgdGhpcyBkYXRhLiBJdCBhbHNvIGxvb2tzIGxpa2UgdGhlIEFDRiBpcyBzaG93aW5nIHNvbWUgc2Vhc29uYWxpdHkgaW4gdGhlIGxhZ3MsIGFzIHRoZXJlIGlzIGEgcGF0dGVybiB0byB0aGUgc3Bpa2VzLiBUaGlzIG1ha2VzIHNlbnNlIGFzIHlvdXIgZWxlY3RyaWNpdHkgdXNhZ2UgY2hhbmdlcyBhcyB0aGUgd2VhdGhlciBjaGFuZ2VzLiAKClRoZSBQQUNGIGRpZXMgZG93biBzbyB3ZSB3aWxsIGlkZW50aWZ5IHRoaXMgbW9kZWwgYXMgYSBNQSgxKSBvciBNQSgyKSBzZWVpbmcgaWYgc2Vhc29uYWwgTUEgb3IgcmVndWxhciBNQSByZXN1bHRzIGluIGEgYmV0dGVyIGFyaW1hIG1vZGVsLiBJIHRoaW5rIGl0IGNvdWxkIHBvc3NpYmx5IGJlIHNlYXNvbmFsaXR5IDQsIHJhdGhlciB0aGFuIHNlYXNvbmFsaXR5IDEyIGFzIHdlbGwuIAoxKSAgQVJJTUEoMSwxLDEpKDEsMSwxKSBbMTJdCjIpICBBUklNQSgxLDEsMikoMSwxLDEpIFsxMl0KMykgIEFSSU1BKDEsMSwxKSgxLDEsMikgWzEyXSAKCjIuIFVzaW5nICoqZmFibGUqKiBmaXQgdGhlIGZvbGxvd2luZyBmaXZlIG1vZGVscyB0byB0aGUgKipEUioqIGRhdGE6IChpKS0oaWlpKSB0aGUgdGhyZWUgbW9kZWxzIHlvdSBwcm9wb3NlIGluICgxKSwgKGl2KSB0aGUgYXV0b21hdGljYWxseSBzZWxlY3RlZCBtb2RlbCBieSB0aGUgQVJJTUEoKSBmdW5jdGlvbiwgYW5kICh2KSB0aGUgYXV0b21hdGljYWxseSBzZWxlY3RlZCBtb2RlbCBieSB0aGUgRVRTKCkgZnVuY3Rpb24uICBSZXBvcnQgdGhlIG5hbWUvb3JkZXIgb2YgZWFjaCBtb2RlbCBhbmQgdGhlIGNvcnJlc3BvbmRpbmcgQUlDYyBhbmQgQklDLgpgYGB7cn0KI2hvdyBtYW55IGRpZmZlcmVuY2VzIGl0IG5lZWRzCkRSICU+JSBmZWF0dXJlcyhFTEVDLCB1bml0cm9vdF9uc2RpZmZzKSAjc2Vhc29uYWwgZGlmZmVyZW5jZXMgbmVlZGVkID0gMQpEUiAlPiUgZmVhdHVyZXMoRUxFQyx1bml0cm9vdF9uZGlmZnMpICNkaWZmZXJlbmNlcyBuZWVkZWQgPSAwCgpmaXREUiA8LSBEUiAlPiUKICBtb2RlbCggI25vbmUgb2YgdGhlIG1vZGVscyB3b3JrZWQgd2l0aCBkID0gMCAtIEhFTFAKICAgIGFyaW1hMSA9IEFSSU1BKEVMRUMgfiBwZHEoMSwxLDEpICsgUERRKDEsMSwxKSksCiAgICBhcmltYTIgPSBBUklNQShFTEVDIH4gcGRxKDEsMSwyKSArIFBEUSgxLDEsMSkpLAogICAgYXJpbWEzID0gQVJJTUEoRUxFQyB+IHBkcSgxLDEsMSkgKyBQRFEoMSwxLDIpKSwKICAgIGF1dG8uYXJpbWEgPSBBUklNQShFTEVDKSwgIzEwMDIxMAogICAgYXV0by5FVFMgPSBFVFMoRUxFQykpICNlcnJvcigiTSIpICsgdHJlbmQoIk4iKSArIHNlYXNvbigiQSIpCiAgICAKZml0RFIgJT4lIHBpdm90X2xvbmdlcihldmVyeXRoaW5nKCksIG5hbWVzX3RvID0gIk1vZGVsIG5hbWUiLAogICAgICAgICAgICAgICAgICAgICB2YWx1ZXNfdG8gPSAiT3JkZXJzIikKCmdsYW5jZShmaXREUikgJT4lIGFycmFuZ2UoQUlDYykgJT4lIHNlbGVjdCgubW9kZWw6QklDKQpgYGAKCjMuIEV4YW1pbmUgdGhlIHJlc2lkdWFscyBvZiBhbGwgdGhlIG1vZGVscyB1c2luZyB0aGUgTGp1bmctQm94IHRlc3QgYW5kIHRoZSAqKmdnX3RzcmVzaWR1YWxzKCkqKiBmdW5jdGlvbi4gSXMgdGhlcmUgYSB2YWxpZGl0eSBwcm9ibGVtIHdpdGggYW55IG9mIHRoZSBtb2RlbHM/CgpgYGB7cn0KZml0RFIgJT4lIGF1Z21lbnQoKSAlPiUKICBmZWF0dXJlcygucmVzaWQsIGxqdW5nX2JveCwgbGFnID0gNjApCgpmaXREUiAlPiUgc2VsZWN0KGFyaW1hMSkgJT4lIGdnX3RzcmVzaWR1YWxzKCkKZml0RFIgJT4lIHNlbGVjdChhcmltYTIpICU+JSBnZ190c3Jlc2lkdWFscygpCmZpdERSICU+JSBzZWxlY3QoYXJpbWEzKSAlPiUgZ2dfdHNyZXNpZHVhbHMoKSAKZml0RFIgJT4lIHNlbGVjdChhdXRvLmFyaW1hKSAlPiUgZ2dfdHNyZXNpZHVhbHMoKQpmaXREUiAlPiUgc2VsZWN0KGF1dG8uRVRTKSAlPiUgZ2dfdHNyZXNpZHVhbHMoKQpgYGAKCjQuIEZvciB0aGUgc2V0IG9mIGZpdmUgbW9kZWxzIHNlbGVjdGVkIChhdXRvbWF0aWNhbGx5IGFuZC9vciBtYW51YWxseSkgIGV4YW1pbmUgdGhlIGluLXNhbXBsZSBhY2N1cmFjeSBtZXRyaWNzLiAgQmFzZWQgb24gYSBob2xpc3RpYyBhbmFseXNpcyBvZiB0aGUgaW5mb3JtYXRpb24gY3JpdGVyaWEgc2VsZWN0IHRoZSBiZXN0IHR3byBBUklNQSBtb2RlbHMgYW5kIHRoZSBFVFMgbW9kZWwuIFJlcG9ydCB0aGUgbW9kZWwgbmFtZS9vcmRlciBhbmQgdGhlaXIgcGFyYW1ldGVyIHZhbHVlcy4KCkZvciBtb2RlbCBjcm9zcy12YWxpZGF0aW9uIHB1cnBvc2VzIHN0cmV0Y2ggdGhlIERSIGRhdGEgYXMgZm9sbG93czoKYGBge3J9CmZpdERSICU+JSBhY2N1cmFjeSgpICU+JSBzZWxlY3QoLm1vZGVsLCBNQVBFLCBSTVNFLCBNQUUpCmZpdERSICU+JSBnbGFuY2UoKSAlPiUKICBzZWxlY3QoLm1vZGVsLCBBSUMsIEFJQ2MsIEJJQykKYGBgCkJlc3QgMiBhcmltYSBNb2RlbHMgPSBhcmltYTMgQVJJTUEoMSwxLDEpKDEsMSwyKSBbMTJdICBhbmQgYXJpbWExIEFSSU1BKDEsMSwxKSgxLDEsMSkgWzEyXQpFVFMgbW9kZWwgPSBFbGVjIH4gZXJyb3IoIk0iKSArIHRyZW5kKCJOIikgKyBzZWFzb24oIkEiKQoKCjUuIEZpdCBjcm9zcy12YWxpZGF0aW9uIG1vZGVscyBmb3IgZWFjaCBvZiB0aGUgdGltZSBzdWItc2VyaWVzIGluIHRoZSBzdHJldGNoZWQgZGF0YSBmb3IgZWFjaCBvZiB0aGUgZm91ciBtb2RlbCB0eXBlcyBzZWxlY3RlZCBpbiAoNCkuIEluIHRoZSBjYXNlKHMpIHdoZXJlIHRoZSBtb2RlbHMgd2VyZSBhdXRvbWF0aWNhbGx5IHNlbGVjdGVkLCBkbyBOT1QgcnVuIHRoZSBhdXRvbWF0aWMgc2VsZWN0aW9uIHVuZGVyIGNyb3NzIHZhbGlkYXRpb24sIGluc3RlYWQgZW50ZXIgbWFudWFsbHkgdGhlIG1vZGVsIG9yZGVyL3R5cGUgd2hlbiB5b3UgY2FsbCB0aGUgQVJJTUEoKS9FVFMoKSBmdW5jdGlvbi4gCgpgYGB7cn0KIzMgb3IgNCBtb2RlbHM/Pwoja2luZGEgdGFrZXMgYXdoaWxlCiNnaXZlbiBjb2RlCkRSLkNWIDwtIERSICU+JQogIGZpbHRlcihEQVRFID49IHllYXJtb250aCgiMjAxMCBKYW4iKSkgJT4lCiAgc3RyZXRjaF90c2liYmxlKC5pbml0ID0gMzYsIC5zdGVwID0gMSkKCm1DIDwtIERSLkNWICU+JSAKICBtb2RlbCgKICAgIGFyaW1hMSA9IEFSSU1BKEVMRUMgfiBwZHEoMCwxLDEpICsgUERRKDAsMSwxKSksCiAgICBhcmltYTIgPSBBUklNQShFTEVDIH4gcGRxKDAsMSwyKSArIFBEUSgwLDEsMSkpLCAjcTQgYXNrZWQgZm9yIDMgbW9kZWxzLCBidXQgaGUgYXNrZWQgZm9yIDQgaGVyZSBzbyBJICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaW5jbHVkZWQgYXJpbWEyIAogICAgYXJpbWEzID0gQVJJTUEoRUxFQyB+IHBkcSgwLDEsMikgKyBQRFEoMCwxLDIpKSwKICAgIEVUUyhFTEVDIH4gZXJyb3IoIk0iKSArIHRyZW5kKCJOIikgKyBzZWFzb24oIkEiKSkpIApgYGAKCjYuIFByZXBhcmUgYSAyNC1tb250aCBhaGVhZCBmb3JlY2FzdCBmb2UgZWFjaCBvZiB0aGUgbW9kZWxzIGZpdHRlZCBpbiAoNSkgYW5kIHByZXBhcmUgYSBwbG90IG9mIE1BUEUgdnMgbW9udGhzLWFoZWFkLiAgQmFzZWQgb24gdGhlIGR5bmFtaWMgYmVoYXZpb3Igb2YgY3Jvc3MtdmFsaWRhdGlvbiBNQVBFIGRpc2N1c3Mgd2hpY2ggbW9kZWwocykgc2hvdWxkIGJlIGtlcHQvZGlzY2FyZGVkLgpgYGB7cn0KZkNWIDwtIG1DICU+JSAKICBmb3JlY2FzdChoID0gMjQpICU+JSAjZm9yIGVhY2ggb2YgdGhlIDQgbW9kZWwgSURzIHlvdSBoYXZlIDI0IGZvcmVjYXN0cyAKICBncm91cF9ieSguaWQsIC5tb2RlbCkgJT4lICNJRCBvZiBvbmUgY29ycmVzcG9uZHMgdG8gMyBtb2RlbHMKICBtdXRhdGUoaCA9IHJvd19udW1iZXIoKSkgJT4lICNjcmVhdGUgdmFyaWFibGUgaCBmb3IgdGhlIC5pZCAjIAogIHVuZ3JvdXAoKSAjZm9yZWNhc3QgY3Jvc3MgdmFsaWRhdGlvbiAKCmZDViAlPiUKICBhY2N1cmFjeShELCBieSA9IGMoImgiLCAiLm1vZGVsIikpICU+JSAjc2hvd3MgeW91IGFsbCB0aGUgQ1YgbWV0cmljcwogIGdncGxvdChhZXMoeCA9IGgsIHkgPSBNQVBFLCBjb2xvciA9IC5tb2RlbCkpICsKICBnZW9tX2xpbmUoKQoKZkNWICU+JQogIGFjY3VyYWN5KEQsIGJ5ID0gYygiaCIsICIubW9kZWwiKSkgJT4lCiAgZ2dwbG90KGFlcyh4ID0gaCwgeSA9IFJNU0UsIGNvbG9yID0gLm1vZGVsKSkgKwogIGdlb21fbGluZSgpCmBgYAoKNy4gRXhhbWluZSB0aGUgY3Jvc3MtdmFsaWRhdGlvbiByZXNpZHVhbHMgb2YgdGhlIG1vZGVscyB5b3Ugc2VsZWN0ZWQgaW4gKDYpLCBhbmQgYmFzZWQgb24gdGhlaXIgY29ycmVsYXRpb24gKG1vZGVsIHZzLiBtb2RlbCkgZGlzY3VzcyBpZiBpdCBpcyBhZHZpc2FibGUgdG8gcHJlcGFyZSBhbiBlbnNlbWJsZSBmb3JlY2FzdCBhdmVyYWdpbmcgdGhlIGZvcmVjYXN0cyBvZiB0d28gb3IgbW9yZSBtb2RlbHMuCmBgYHtyfQpEUi5DViA8LSBEUiAlPiUKICBmaWx0ZXIoREFURSA+PSB5ZWFybW9udGgoIjIwMTAgSmFuIikpICU+JQogIHN0cmV0Y2hfdHNpYmJsZSguaW5pdCA9IDM2LCAuc3RlcCA9IDEpCgpEUi5DViAlPiUgCiAgbW9kZWwoCiAgICBBUklNQShFTEVDIH4gcGRxKDAsMSwyKSArIFBEUSgwLDEsMikpLAogICAgRVRTKEVMRUMgfiBlcnJvcigiTSIpICsgdHJlbmQoIk4iKSArIHNlYXNvbigiQSIpKSkgJT4lCiAgZm9yZWNhc3QoaCA9IDI0KSAlPiUKICBhY2N1cmFjeShEUikgJT4lCiAgc2VsZWN0KC5tb2RlbCwgUk1TRTpNQVBFKQpgYGAKCjguIFRoZSBpbmRleCBpcyB2ZXJ5IHVzZWZ1bCBmb3IgZW5lcmd5IHBsYW5uaW5nIHB1cnBvc2UgYXMgbW9zdCBvZiB0aGUgdmFyaWFiaWxpdHkgYW5kIHNlYXNvbmFsaXR5IGlzIHByb2R1Y2VkIGJ5IGNvbWJpbmVkIGN5Y2xlIG5hdHVyYWwgZ2FzIHBsYW50cyBhbmQgc2luZ2xlIGN5Y2xlIHBlYWtlciBwbGFudHMgdGhhdCBhbHNvIHJ1biBvbiBuYXR1cmFsIGdhcyAoaS5lLiwgbnVjbGVhciBhbmQgY29hbCBnZW5lcmF0aW9uIGlzIGZpeGVkIGFuZCByZWxhdGl2ZWx5IGNvbnN0YW50KS4gIEZvciB0aGlzIHB1cnBvc2UgaXQgaXMgb2YgaW50ZXJlc3QgdG8ga25vdyB3aGF0IGlzIHRoZSBwcm9kdWN0aW9uIGluZGV4IGxldmVsIHRoYXQgd2lsbCBub3QgYmUgc3VwZXJhdGVkIHdpdGggYSBwcm9iYWJpbGl0eSAoc2VydmljZS1sZXZlbCkgb2YgOTUlLiBGb3IgdGhlIGJlc3QgbW9kZWwgaW4gKDYpIHBsb3QgdGhlIDI0LW1vbnRoIGFoZWFkIGZvcmVjYXN0IGFuZCBwbG90IHRoZSBmb3JlY2FzdCBhbmQgdGhlIGNvcnJlc3BvbmRpbmcgY29uZmlkZW5jZSBpbnRlcnZhbCB0byBoZWxwIHlvdSBhZGRyZXNzIHRoZSBzZXJ2aWNlIGxldmVsIHF1ZXN0aW9uLiBSZXBvcnQgbnVtZXJpY2FsbHkgdGhlIG1vbnRoLWJ5LW1vbnRoIHRoZSBpbmRleCBmb3JlY2FzdHMgdGhhdCBtZWV0IHRoZSBkZXNpcmVkIDk1JSBzZXJ2aWNlIGxldmVsLgo=